package sketch.specs;

import java.util.LinkedList;
import java.util.List;

import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import org.eclipse.jdt.core.dom.VariableDeclarationStatement;

import sketch.generator.ArrayValueGenerator;
import sketch.generator.ChainValueGenerator;
import sketch.generator.ExhaustiveValueGenerator;
import sketch.generator.RandomValueGenerator;
import sketch.generator.ValueGenerator;
import sketch.generator.experiment.ChainValueGroupGenerator;
import sketch.generator.experiment.ExhaustiveValueGroupGenerator;
import sketch.generator.experiment.SubsetValueGroupGenerator;
import sketch.generator.experiment.ValueGroupGenerator;
import sketch.ounit.Values;
import sketch.util.Checker;

public class DeclaredValueExtractor {
	
	public final VariableDeclarationFragment statementFragment;
	
	private MethodInvocation methodInvokeInVarDecl; 
	
	public DeclaredValueExtractor(VariableDeclarationFragment statementFragment) {
		Checker.checkNull(statementFragment, "The statementFragment input could not be null.");
		this.statementFragment = statementFragment;
	}
	
	public ValueGenerator<Expression> getArgumentValueGenerator() {
		List<Expression> args = this.getArgumentExpression();
		Checker.checkNull(methodInvokeInVarDecl, "The methodInvokeInVarDecl should not be null.");
		ValueGenerator generator = null;
		//get the method name
		String methodName = this.methodInvokeInVarDecl.getName().toString();
		
		if(methodName.equals(Values.choose)) {
			generator = new ChainValueGenerator<Expression>(args);
		} else if (methodName.equals("chooseArray")) {
			generator = new ArrayValueGenerator<Expression>(args);
		} else if (methodName.equals("randomChoose")) {
			generator = new RandomValueGenerator<Expression>(args);
		} else if (methodName.equals(Values.exhaust)) {
			//XXX FIXME this is somehow ugly
			generator = new ExhaustiveValueGenerator<Expression>(args);
		} else {
			throw new RuntimeException("Unexpected method name: " + methodName);
		}
		
		Checker.checkNull(generator, "The value generator should not be null!");
		return generator;
	}
	
	public ValueGroupGenerator<Expression> getArgumentGroupValueGenerator() {
		List<Expression> args = this.getArgumentExpression();
		Checker.checkNull(methodInvokeInVarDecl, "The methodInvokeInVarDecl should not be null.");
		ValueGroupGenerator<Expression> generator = null;
		//get the method name
		String methodName = this.methodInvokeInVarDecl.getName().toString();
		if(methodName.equals(Values.choose)) {
			generator = new ChainValueGroupGenerator<Expression>(args);
		}  else if (methodName.equals(Values.exhaust)) {
			//XXX FIXME this is somehow ugly
			generator = new ExhaustiveValueGroupGenerator<Expression>(args);
		} else if(methodName.equals(Values.subset)) {
			generator = new SubsetValueGroupGenerator<Expression>(args);
		} else {
			throw new RuntimeException("Unexpected method name: " + methodName);
		}
		return generator;
	}
	
	public List<Expression> getArgumentExpression() {
		List<Expression> retList = new LinkedList<Expression>();

//		List<VariableDeclarationFragment> fragments = statement.fragments();
//		Checker.checkTrue(fragments.size() == 1,
//				"The size of fragments should be 1.");
		// for(VariableDeclarationFragment fragment : fragments) {
		// System.out.println(" fragment: " + fragment.getInitializer());
		Expression expr = this.statementFragment.getInitializer();
		Checker.checkTrue((expr instanceof MethodInvocation),
				"Wrong type, expr is not a method invocation, it is: " + expr.getClass());
		this.methodInvokeInVarDecl = (MethodInvocation) expr;
		List<Expression> arguments = this.methodInvokeInVarDecl.arguments();
		retList.addAll(arguments);
		// }

		return retList;
	}
	
	private void checkValidity(VariableDeclarationStatement statement) {
		Checker.checkTrue(SpecValueDeclarationFinder.isValuesStatement(statement),
				"The statement: " + statement + " is not valid");
	}
}